import 'dart:convert';
import 'dart:io';
import 'package:args/args.dart';

// Constant for the command-line flag to enable line numbers
const lineNumberOption = 'numbers';

// Main function to parse command-line arguments and start file content display
void main(List<String> args) {
  // Set default exit code to 0 (success)
  exitCode = 0;

  // Create argument parser with a flag for line numbers (--numbers or -n)
  final parser =
      ArgParser()..addFlag(lineNumberOption, negatable: false, abbr: 'n');

  // Parse command-line arguments
  ArgResults argResults = parser.parse(args);

  // Get remaining arguments (file paths)
  final filePaths = argResults.rest;

  // Call function to display file content or handle input
  displayFileContent(
    filePaths,
    showLineNumbers: argResults[lineNumberOption] as bool,
  );
}

// Asynchronous function to display file content or read from standard input
// [filePaths]: List of file paths to read
// [showLineNumbers]: Whether to display line numbers
Future<void> displayFileContent(
  List<String> filePaths, {
  bool showLineNumbers = false,
}) async {
  // Set encoding to UTF-8 for consistent text handling
  stdin.lineMode = true;
  stdout.encoding = utf8;
  stderr.encoding = utf8;

  if (filePaths.isEmpty) {
    // Interactive mode: read from standard input if no file paths are provided
    print('Enter text (type "exit" to quit):');
    while (true) {
      stdout.write('> '); // Prompt for input without newline
      String? input = stdin.readLineSync(
        encoding: utf8,
      ); // Read input with UTF-8
      if (input == null) {
        print('Invalid input, please try again.');
        continue;
      }
      stdout.writeln(input); // Echo input

      // Check for exit command (case-insensitive)
      if (input.toLowerCase() == 'exit') {
        print('Program terminated.');
        break;
      }
    }
  } else {
    // File mode: read and display content from each file
    for (final path in filePaths) {
      var lineCount = 1; // Line number counter
      // Create a stream to read file content, decoded as UTF-8 and split into lines
      final lines = utf8.decoder
          .bind(File(path).openRead())
          .transform(const LineSplitter());

      try {
        // Read and output each line asynchronously
        await for (final line in lines) {
          if (showLineNumbers) {
            stdout.write('$lineCount '.padLeft(4)); // Left-align line number
            lineCount++;
          }
          stdout.writeln(line); // Output the line
        }
      } catch (e) {
        // Handle file reading errors
        await _handleFileError(path, e);
      }
    }
  }
}

// Asynchronous function to handle file reading errors
// [path]: File path that caused the error
// [error]: The caught exception
Future<void> _handleFileError(String path, dynamic error) async {
  // Ensure error output uses UTF-8
  stderr.encoding = utf8;
  if (await FileSystemEntity.isDirectory(path)) {
    stderr.writeln('Error: "$path" is a directory, cannot read.');
  } else {
    stderr.writeln('Failed to read file "$path": $error');
    exitCode = 2; // Set error exit code
  }
}
